#ifndef TCP_SERVER_LOGGING_H
#define TCP_SERVER_LOGGING_H

#include <cstdio>
#include "../Timestamp.h"
#include "LogStream.h"

class Logger {
public:
    enum LogLevel {
        TRACE, // 追踪
        DEBUG,
        INFO, // 基本
        WARN, // 警告
        ERROR,
        FATAL, // 失败, 输出这种类型的日志会直接停用程序
        NUM_LOG_LEVELS, // 用日志等级数量, 通过自动转化来获取
    };

    /**
     * 用于在编译的时候计算源文件的基本名称
     */
    class SourceFile {
    public:
        template<int N>
        SourceFile(const char (&arr)[N])
                : data_(arr),
                  size_(N - 1) {
            const char *slash = strrchr(data_, '/'); // builtin function
            if (slash) {
                data_ = slash + 1;
                size_ -= static_cast<int>(data_ - arr);
            }
        }

        explicit SourceFile(const char *filename)
                : data_(filename) {
            const char *slash = strrchr(filename, '/');
            if (slash) {
                data_ = slash + 1;
            }
            size_ = static_cast<int>(strlen(data_));
        }

        const char *data_;
        int size_;
    };

    /**
     * 写入日志的资源文件和日志级别
     */
    Logger(SourceFile file, int line);

    Logger(SourceFile file, int line, LogLevel level);

    Logger(SourceFile file, int line, LogLevel level, const char *func);

    Logger(SourceFile file, int line, bool toAbort);

    ~Logger();

    /**
     * 获取日志流
     */
    LogStream &stream() { return impl_.stream_; }

    /**
     * 获取日志库设定的日志等级, 当设定的等级很高的时候低等级的日志信息不会被记录
     */
    static LogLevel logLevel();

    /**
     * 设置总的日志等级
     */
    static void setLogLevel(LogLevel level);

    /* 输入输出函数 */
    typedef void (*OutputFunc)(const char *msg, int len);

    typedef void (*FlushFunc)();

    static void setOutput(OutputFunc);

    static void setFlush(FlushFunc);

    static void setIsAsync(bool isAsync);

//    static void setTimeZone(const TimeZone &tz);

private:
    class Impl {
    public:
        typedef Logger::LogLevel LogLevel;

        /**
         * 初始化日志流实现
         * @param level 日志等级
         * @param old_errno 错误标识, 如果不为0的话会将对应的错误信息输入
         * @param file 日志记录时对应的文件名, 这里会通过C++的 define宏定义 __FILE__输入
         * @param line 日志执行时对应的行, 会通过 define 宏定义的 __LINE__获取
         */
        Impl(LogLevel level, int old_errno, const SourceFile &file, int line);


        void formatTime();

        /**
         * 日志的结束部分, 会将对应的日志文件名 : 行数输出到日志的末尾
         */
        void finish();

        Timestamp time_;
        LogStream stream_;
        LogLevel level_;
        int line_;
        SourceFile basename_;
        // 基本的资源文件名
    };

    Impl impl_;

};

extern Logger::LogLevel g_logLevel;

inline Logger::LogLevel Logger::logLevel() {
    return g_logLevel;
}

//
// CAUTION: do not write:
//
// if (good)
//   LOG_INFO << "Good news";
// else
//   LOG_WARN << "Bad news";
//
// this expends to
//
// if (good)
//   if (logging_INFO)
//     logInfoStream << "Good news";
//   else
//     logWarnStream << "Bad news";
//
/* 下面是宏定义的日志输入, 在输入之前会进行日志等级的判断, 如果设定的日志库等级高于当前日志等级那当前输入的日志不会被记录
 * 会直接执行一个空操作, 不会浪费资源*/
#define LOG_TRACE if (Logger::logLevel() <= Logger::TRACE) \
  Logger(__FILE__, __LINE__, Logger::TRACE, __func__).stream()
#define LOG_DEBUG if (Logger::logLevel() <= Logger::DEBUG) \
  Logger(__FILE__, __LINE__, Logger::DEBUG, __func__).stream()
#define LOG_INFO if (Logger::logLevel() <= Logger::INFO) \
  Logger(__FILE__, __LINE__).stream()
// 上面的if判断会在日志等级小于对应等级的情况下才进行输出
#define LOG_WARN Logger(__FILE__, __LINE__, Logger::WARN).stream()
#define LOG_ERROR Logger(__FILE__, __LINE__, Logger::ERROR).stream()
#define LOG_FATAL Logger(__FILE__, __LINE__, Logger::FATAL).stream()
#define LOG_SYSERR Logger(__FILE__, __LINE__, false).stream()
#define LOG_SYSFATAL Logger(__FILE__, __LINE__, true).stream()

const char *strerror_tl(int savedErrno);

// Taken from glog/logging.h
//
// Check that the input is non NULL.  This very useful in constructor
// initializer lists.



// A small helper for CHECK_NOTNULL().
template<typename T>
T *CheckNotNull(Logger::SourceFile file, int line, const char *names, T *ptr) {
    if (ptr == NULL) {
        Logger(file, line, Logger::FATAL).stream() << names;
    }
    return ptr;
}

#define CHECK_NOTNULL(val) \
  CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val))

#endif //TCP_SERVER_LOGGING_H
